﻿using System;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Windows.Networking;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;

namespace LimitlessConnector
{
  /// <summary>
  /// Connector for Limitless RGBW-type bulbs
  /// </summary>
  public class RGBWConnector : IRGBWConnector
  {
    private String m_HostName = null;
    private String m_Port = null;

    /// <summary>
    /// Creates a new instance of the connector and applies the network settings for the wifi-bridge.
    /// </summary>
    /// <param name="HostName">Name of the host.</param>
    /// <param name="Port">The port.</param>
    public RGBWConnector(String HostName, String Port)
    {
      m_HostName = HostName;
      m_Port = Port;
    }

    /// <summary>
    /// Gets a value indicating whether the connector already [has network settings].
    /// </summary>
    /// <value>
    ///   <c>true</c> if [has network settings]; otherwise, <c>false</c>.
    /// </value>
    public bool HasNetworkSettings
    {
      get
      {
        return (!String.IsNullOrWhiteSpace(m_HostName) && !String.IsNullOrWhiteSpace(m_Port));
      }
    }

    /// <summary>
    /// Updates the bridges network-settings.
    /// </summary>
    /// <param name="HostName">Name of the host.</param>
    /// <param name="Port">The port.</param>
    public void UpdateBridgeSettings(String HostName, String Port)
    {
      m_HostName = HostName;
      m_Port = Port;
    }

    /// <summary>
    /// Enables all groups.
    /// </summary>
    /// <returns></returns>
    public async Task EnableAll()
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          writer.WriteBytes(LimitlessRGBWCommands.Enable_All);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Disables all groups.
    /// </summary>
    /// <returns></returns>
    public async Task DisableAll()
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          writer.WriteBytes(LimitlessRGBWCommands.Disable_All);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Enables the selected group.
    /// </summary>
    /// <param name="GroupNo">The group no. Valid values are 1 to 4.</param>
    /// <returns></returns>
    /// <exception cref="System.NotSupportedException">Only group numbers 1 to 4 are supported.</exception>
    public async Task EnableGroup(int GroupNo)
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          byte[] LimitlessCommand;
          if (GroupNo == 1)
            LimitlessCommand = LimitlessRGBWCommands.Enable_Group1;
          else if (GroupNo == 2)
            LimitlessCommand = LimitlessRGBWCommands.Enable_Group2;
          else if (GroupNo == 3)
            LimitlessCommand = LimitlessRGBWCommands.Enable_Group3;
          else if (GroupNo == 4)
            LimitlessCommand = LimitlessRGBWCommands.Enable_Group4;
          else
            throw new NotSupportedException("Only group numbers 1 to 4 are supported.");
          writer.WriteBytes(LimitlessCommand);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Disables the selected group.
    /// </summary>
    /// <param name="GroupNo">The group no. Valid values are 1 to 4.</param>
    /// <returns></returns>
    /// <exception cref="System.NotSupportedException">Only group numbers 1 to 4 are supported.</exception>
    public async Task DisableGroup(int GroupNo)
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          byte[] LimitlessCommand;
          if (GroupNo == 1)
            LimitlessCommand = LimitlessRGBWCommands.Disable_Group1;
          else if (GroupNo == 2)
            LimitlessCommand = LimitlessRGBWCommands.Disable_Group2;
          else if (GroupNo == 3)
            LimitlessCommand = LimitlessRGBWCommands.Disable_Group3;
          else if (GroupNo == 4)
            LimitlessCommand = LimitlessRGBWCommands.Disable_Group4;
          else
            throw new NotSupportedException("Only group numbers 1 to 4 are supported.");
          writer.WriteBytes(LimitlessCommand);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Enables the pre-configured program mode.
    /// </summary>
    /// <returns></returns>
    public async Task EnableProgramMode()
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          writer.WriteBytes(LimitlessRGBWCommands.Program);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Increases the speed of the program.
    /// </summary>
    /// <returns></returns>
    public async Task IncreaseSpeed()
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          writer.WriteBytes(LimitlessRGBWCommands.SpeedIncrease);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Decreases the speed of the program.
    /// </summary>
    /// <returns></returns>
    public async Task DecreaseSpeed()
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          writer.WriteBytes(LimitlessRGBWCommands.SpeedDecrease);
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Sets the brightness.
    /// </summary>
    /// <param name="GroupNo">The group no. Values from 1 to 4 are valid.</param>
    /// <param name="Brightness">The brightness. Values from 2 to 27 are valid.</param>
    /// <returns></returns>
    /// <exception cref="System.NotSupportedException">
    /// Only group numbers 1 to 4 are supported.
    /// or
    /// Only brightness-values from 2 to 27 are supported.
    /// </exception>
    public async Task SetBrightness(int GroupNo, int Brightness)
    {
      if (GroupNo < 1 || GroupNo > 4)
        throw new NotSupportedException();
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      if (Brightness < 2 || Brightness > 27)
        throw new NotSupportedException();

      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          await EnableGroup(GroupNo);
          Thread.Sleep(100);
          writer.WriteBytes(LimitlessRGBWCommands.GetBrightnessCommand(Brightness));
          await writer.StoreAsync();
        }
      }
    }


    /// <summary>
    /// Sets the color for the specified group.
    /// </summary>
    /// <param name="GroupNo">The group no. Valid values are 1 to 4.</param>
    /// <param name="Color">The color. Valid values are 0 to 255.</param>
    /// <returns></returns>
    /// <exception cref="System.NotSupportedException">
    /// Only group numbers 1 to 4 are supported.
    /// or
    /// Only color values between 0 and 255 are supported.
    /// </exception>
    public async Task SetColor(int GroupNo, int Color)
    {
      if (GroupNo < 1 || GroupNo > 4)
        throw new NotSupportedException();
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      if (Color < 0 || Color > 255)
        throw new NotSupportedException();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          await EnableGroup(GroupNo);
          Thread.Sleep(100);
          writer.WriteBytes(LimitlessRGBWCommands.GetColorCommand(Color));
          await writer.StoreAsync();
        }
      }
    }

    /// <summary>
    /// Enables the white LEDs in the bulb for all groups
    /// </summary>
    /// <returns></returns>
    public async Task SetWhite()
    {
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      await SetWhite(0);
    }

    /// <summary>
    /// Enables the white LEDs in the bulb for the selected group
    /// </summary>
    /// <param name="GroupNo">The group no. 1 to 4 are valid values.</param>
    /// <returns></returns>
    /// <exception cref="System.NotSupportedException">Only group numbers 1 to 4 are supported.</exception>
    public async Task SetWhite(int GroupNo)
    {
      if (GroupNo < 0 || GroupNo > 4)
        throw new NotSupportedException();
      if (!HasNetworkSettings)
        throw new NetworkSettingsEmptyExceptions();
      var socket = new DatagramSocket();
      using (var stream = await socket.GetOutputStreamAsync(new HostName(m_HostName), m_Port))
      {
        using (var writer = new DataWriter(stream))
        {
          if (GroupNo == 0)
          {
            writer.WriteBytes(new byte[] { 0x42, 0x00, 0x55 });
            await writer.StoreAsync();
            Thread.Sleep(100);
            writer.WriteBytes(new byte[] { 0xC2, 0x00, 0x55 });
            await writer.StoreAsync();
          }
          else if (GroupNo == 1)
          {
            writer.WriteBytes(new byte[] { 0x45, 0x00, 0x55 });
            await writer.StoreAsync();
            Thread.Sleep(100);
            writer.WriteBytes(new byte[] { 0xC5, 0x00, 0x55 });
            await writer.StoreAsync();
          }
          else if (GroupNo == 2)
          {
            writer.WriteBytes(new byte[] { 0x47, 0x00, 0x55 });
            await writer.StoreAsync();
            Thread.Sleep(100);
            writer.WriteBytes(new byte[] { 0xC7, 0x00, 0x55 });
            await writer.StoreAsync();
          }
          else if (GroupNo == 3)
          {
            writer.WriteBytes(new byte[] { 0x49, 0x00, 0x55 });
            await writer.StoreAsync();
            Thread.Sleep(100);
            writer.WriteBytes(new byte[] { 0xC9, 0x00, 0x55 });
            await writer.StoreAsync();
          }
          else if (GroupNo == 4)
          {
            writer.WriteBytes(new byte[] { 0x4B, 0x00, 0x55 });
            await writer.StoreAsync();
            Thread.Sleep(100);
            writer.WriteBytes(new byte[] { 0xCB, 0x00, 0x55 });
            await writer.StoreAsync();
          }

          await writer.StoreAsync();
        }
      }
    }

  }
}
